<?php
/**
 * Данный класс наследует свойства и методы стандартного класса PDO и представляет собой адаптер для обмена данными между "движком" гостевой книги gbook.php и выбранным драйвером PHP_PDO
 *  
 * Класс содержит закрытые свойства, открытые и закрытые методы
 *
 * @author Massa
 */

class GbPDO extends PDO {

	/**
	 * Получает объект класса GbPDO 
     *
	 * @var GbPDO
 	 */
	private $_db;

	/**
	 * Получает имя пользователя БД 
     *
	 * @var string
 	 */	
	private $_user;

	/**
	 * Получает пароль пользователя БД 
     *
	 * @var string
 	 */	
	private $_pass;

	/**
	 * Получает имя БД 
     *
	 * @var string
 	 */	
	private $_dbname;

	/**
	 * Получает имя хоста 
     *
	 * @var string
 	 */	
	private $_host;

	/**
	 * Получает имя драйвера PDO 
     *
	 * @var string
 	 */	
	private $_drv;

	/**
     * Конструктор объекта класса
     *
	 * Объект представляет собой открытое соединение с БД (SQL) выбранного драйвера PDO ($eng), с именем $dbname и двумя таблицами: gb_messages и gb_comments 
	 * Создание объекта осуществляется путем выполнения серии запросов SQL
     * Подготовка запросов производится записью их текста в переменную $sql, а исполнение - вызовом метода queryHandle()
     */
	public function __construct($eng,$host,$user,$pass,$dbname) {

		// Инициализация свойств

		$this->_user = $user;
		$this->_pass = $pass;
		$this->_dbname = $dbname;
		$this->_host = $host;
		$this->_drv = "mysql";

		// Создание объекта класса GbPDO и подключение к БД с проверкой на ошибки с помощью объекта PDOException
		
        try {

			$this->_db = new PDO (self::getDSN (), $this->_user, $this->_pass);

		} catch (PDOException $e) {

			echo 'Подключение не удалось: ' . $e->getMessage();
			die();

		}

		// Создание БД, если она еще не была создана, с именем _dbname
		// Параметрами задаются: набор символов UTF-8 и правило сортировки utf8_general_ci
		// Эти параметры необходимо задавать сразу, во избежание проблем в дальнейшем, когда в таблицах уже будет информация

		$sql = "CREATE DATABASE IF NOT EXISTS ". $this->_dbname. " CHARACTER SET utf8 COLLATE utf8_general_ci";

		$result = $this->queryHandle($sql);

		// Переключение в контекст только что созданной БД

		$sql = "USE ". $this->_dbname;

		$result = $this->queryHandle($sql);

		// Задание параметра для сохранения в БД информации в правильной кодировке UTF-8
		
		$sql = "SET NAMES utf8";

		$result = $this->queryHandle($sql);

		// Создание таблицы gb_messages, если еще не была создана, для хранения постов
		// Поля: id - главный ключ с автоинкрементом, name - имя пользователя, post - текст сообщения поста, datetime - время публикации в формате TIMESTAMP
						
		$sql = "CREATE TABLE IF NOT EXISTS gb_messages (
					id INT(11) NOT NULL auto_increment,
					PRIMARY KEY (id),
					name TEXT NOT NULL,
					post TEXT,
					datetime INT(11))";
				
		$result = $this->queryHandle($sql);

		// Создание таблицы gb_comments, если еще не была создана, для хранения комментариев
		// Поля: id - главный ключ с автоинкрементом, name - имя пользователя, comment - текст комментария, pid - идентификатор родительского поста - соответствует идентификатору поста таблицы gb_messages, datetime - время публикации в формате TIMESTAMP
		
		$sql = "CREATE TABLE IF NOT EXISTS gb_comments (
					id INT(11) NOT NULL auto_increment,
					PRIMARY KEY (id),
					name TEXT NOT NULL,
					comment TEXT,
					pid INT(11) NOT NULL,
					datetime INT(11))"; 

		$result = $this->queryHandle($sql);
	}

	/**
	 * Деструктор объекта класса
     *
	 * Объект уничтожается путем присвоения значения NULL содержащему его свойству
 	 */

	public function __destruct(){
		$this->_db = NULL;
	}

	/**
	 * Метод добавления поста в БД
     *
	 * Принимает имя пользователя ($name) и текст сообщения ($post) 
	 * Возвращает результат запроса на добавление строки в таблицу, true - успех, false - ошибка
     * Подготовка запроса производится с помощью встроенного метода prepare(), а исполнение - вызовом метода queryHandle()
	 *
	 * @param string $name
	 * @param string $post
	 * @return bool
 	 */
	public function postSave($name, $post) {

		// Очистка поступивших строк имени пользователя и текста сообщения: 
		// удаление тэгов, пробелов и служебных символов (таких как "\n"), защита экранированием вредоносных символов (addslashes),
		// а также, таких символов, как обратный слэш и кавычки  

		$name = addslashes(trim(strip_tags($name)));
		$post = addslashes(trim(strip_tags($post)));

		// Получение TIMESTAMP

		$dt = time();

		// Использование встроенного метода PDOStatement::bindParam() и конструкций PDO::prepare() -- PDO::execute() позволяет избежать SQL-инъекций и отказаться от таких функций, как mysqli_real_escape_string()
		
		try {
			$sql = $this->_db->prepare(
				"INSERT INTO gb_messages(
					name,
					post,
					datetime
				) VALUES (
					:name,
					:post,
					:dt)"
			);
			$sql->bindParam(':name', $name, PDO::PARAM_STR);
			$sql->bindParam(':post', $post, PDO::PARAM_STR);
			$sql->bindParam(':dt', $dt, PDO::PARAM_INT);							
		
			$sql->execute();
		
		} catch (PDOException $e) {
			echo "Запрос возвратил ошибку: (" . $e->getCode() . ") " . $e->getMessage() . "\n";
			die();
		}
		 
	}

	/**
	 * Метод обновления поста в БД
     *
	 * Принимает идентификатор поста ($id) и текст сообщения ($post) 
	 * Возвращает результат запроса на обновление записи в таблице, true - успех, false - ошибка
     * Подготовка запроса производится с помощью встроенного метода prepare(), а исполнение - вызовом метода queryHandle()
	 *
	 * @param integer $id
	 * @param string $post
	 * @return bool
 	 */
	public function postUpdate($id, $post) {

		// Приведение поступившего идентификатора поста к целому положительному типу

		$id = abs((int)$id);
		
		// Очистка поступившей строки текста сообщения: 
		// удаление тэгов, пробелов и служебных символов (таких как "\n"), защита экранированием вредоносных символов (addslashes),
		// а также, таких символов, как обратный слэш и кавычки 
		
		$post = addslashes(trim(strip_tags($post)));

		// Использование встроенного метода PDOStatement::bindParam() и конструкций PDO::prepare() -- PDO::execute() позволяет избежать SQL-инъекций и отказаться от таких функций, как mysqli_real_escape_string()

		try {
			$sql = $this->_db->prepare(
					"UPDATE gb_messages SET
							post = :post
						WHERE
							id = :id"
			);
			$sql->bindParam(':post', $post, PDO::PARAM_STR);
			$sql->bindParam(':id', $id, PDO::PARAM_INT);							
		
			$sql->execute();
		
		} catch (PDOException $e) {
			echo "Запрос возвратил ошибку: (" . $e->getCode() . ") " . $e->getMessage() . "\n";
			die();
		}
	}
				
	/**
	 * Метод получения всех постов из БД запросом SELECT
     *
     * Подготовка запроса производится записью его текста в переменную $sql, а исполнение - вызовом метода queryHandle()
	 *
	 * @return array
 	 */
	public function getAllPosts() {

		// Данные сортируются по убыванию идентификатора, т.е. в обратном порядке занесения в БД 

		$sql = "SELECT id, name, post, datetime FROM gb_messages ORDER BY id DESC";

		// Преобразование объекта в массив с помощью метода PDOStatement::fetchAll() 

		return $this->queryHandle($sql)->fetchAll();

	}

	/**
	 * Метод получения выбранного поста из БД
     *
	 * Принимает идентификатор поста ($id) 
	 * Возвращает результат запроса в виде массива значений поста
     * Подготовка запроса производится записью его текста в переменную $sql, а исполнение - вызовом метода queryHandle()
	 *
	 * @param integer $id
	 * @return array
 	 */
	public function getPost($id) {

		// Приведение поступившего идентификатора поста к целому положительному типу

		$id = abs((int)$id);	
	
		$sql = "SELECT name, post, datetime FROM gb_messages WHERE id = $id";

		// Преобразование объекта в массив с помощью метода PDOStatement::fetchAll()

		return $this->queryHandle($sql)->fetchAll();
	}
	
	/**
	 * Метод получения всех комментариев к выбранному посту из БД
     *
	 * Принимает идентификатор поста ($id) 
	 * Возвращает результат запроса в виде массива полей комментариев
     * Подготовка запроса производится записью его текста в переменную $sql, а исполнение - вызовом метода queryHandle()
	 *
	 * @param integer $id
	 * @return array
 	 */
	public function getAllComments($id) {

		// Приведение поступившего идентификатора поста к целому положительному типу

		$id = abs((int)$id);	

		// Данные сортируются в порядке занесения комментариев в БД

		$sql = "SELECT id, name, comment, datetime FROM gb_comments WHERE pid = $id ORDER BY id";

		// Преобразование объекта в массив с помощью метода PDOStatement::fetchAll()

		return $this->queryHandle($sql)->fetchAll();
	}

	/**
	 * Метод удаления выбранного поста из БД
     *
	 * Принимает идентификатор поста ($id) 
	 * Возвращает результат запроса на удаление записи в таблице, true - успех, false - ошибка
     * Подготовка запроса производится записью его текста в переменную $sql, а исполнение - вызовом метода queryHandle()
	 *
	 * @param integer $id
	 * @return bool
 	 */
	public function postDelete($id){

		// Приведение поступившего идентификатора поста к целому положительному типу

		$id = abs((int)$id);		

		$sql = "DELETE FROM gb_messages WHERE id = $id";
		
		return $this->queryHandle($sql);
	}

	/**
	 * Метод добавления комментария к выбранному посту в БД
     *
	 * Принимает имя пользователя ($name), текст комментария ($comment) и идентификатор поста ($pid) 
	 * Возвращает результат запроса на добавление записи в таблицу, true - успех, false - ошибка
     * Подготовка запроса производится с помощью встроенного метода PDO::prepare(), а исполнение - вызовом метода queryHandle()
	 *
	 * @param string $name
	 * @param string $comment
	 * @param integer $pid
	 * @return bool
 	 */
	public function commentAdd($name, $comment, $pid) {

		// Приведение поступившего идентификатора поста к целому положительному типу

		$pid = abs((int)$pid);		

		// Очистка поступивших строк имени пользователя и текста комментария: 
		// удаление тэгов, пробелов и служебных символов (таких как "\n"), защита экранированием вредоносных символов (addslashes),
		// а также, таких символов, как обратный слэш и кавычки

		$name = addslashes(trim(strip_tags($name)));
		$comment = addslashes(trim(strip_tags($comment)));

		// Получение TIMESTAMP

		$dt = time();

		// Использование встроенного метода PDOStatement::bindParam() и конструкций PDO::prepare() -- PDO::execute() позволяет избежать SQL-инъекций и отказаться от таких функций, как mysqli_real_escape_string()

		try {
			$dt = time();
			$sql = $this->_db->prepare("INSERT INTO gb_comments(
								name,
								comment,
								pid,
								datetime
							) VALUES (
								:name,
								:comment,
								:pid,
								:dt)");
			$sql->bindParam(':name', $name, PDO::PARAM_STR);
			$sql->bindParam(':comment', $comment, PDO::PARAM_STR);
			$sql->bindParam(':pid', $pid, PDO::PARAM_INT);
			$sql->bindParam(':dt', $dt, PDO::PARAM_INT);
			return $sql->execute();
		} catch (PDOException $e) {
			return "Запрос возвратил ошибку: (" . $e->getCode() . ") " . $e->getMessage() . "\n";
			die();
		}
	}
		
	/**
	 * Метод удаления комментария/комментариев к выбранному посту в БД
     *
	 * Допускает удаление выбранного комментария или всех комментариев к заданному посту
	 * Принимает идентификатор комментария ($id) и идентификатор поста ($pid) 
	 * Возвращает результат запроса на удаление записи из таблицы, true - успех, false - ошибка
     * Подготовка запроса производится с помощью встроенного метода PDO::prepare() со вставкой переменных методом PDOStatement::bindParam(), а исполнение - методом PDOStatement::execute()
	 *
	 * @param integer $id
	 * @param integer $pid
	 * @return bool
 	 */
	public function commentRemove($id, $pid=0) {

		// Приведение поступивших идентификаторов поста к целому положительному типу

		$id = abs((int)$id);
		$pid = abs((int)$pid);		

		try {

		// Составление запроса на удаление в соответствии с условием:

			if (!$pid){

		// если идентификатор поста не получен, т.е. $pid = 0 (по умолчанию),
		// удаляется один выбранный комментарий

				$sql = $this->_db->prepare("DELETE FROM gb_comments WHERE id = :id");
				$sql->bindParam(':id', $id, PDO::PARAM_INT); 

			}else{

		// если получен отличный от нуля идентификатор поста,
		// удаляются все комментарии к посту

				$sql = $this->_db->prepare("DELETE FROM gb_comments WHERE pid = :id");
				$sql->bindParam(':id', $pid, PDO::PARAM_INT);
			}
						
			return $sql->execute();
		
		} catch (PDOException $e) {
			echo "Запрос возвратил ошибку: (" . $e->getCode() . ") " . $e->getMessage() . "\n";
			die();
		}
	}	

	/**
	 * Закрытый метод обработки запросов к БД
     *
	 * Принимает текст запроса SQL ($qry) 
	 * Возвращает результат запроса в виде объекта (для запросов SELECT) или булева значения true (удача)
     * В случае неудачи, вызывается объект PDOExtension()
	 *
	 * @param string $qry
	 * @return bool|obj
 	 */
	private function queryHandle($qry) {
		try {
			$res = $this->_db->query($qry);
			return $res;
		} catch (PDOException $e) {
			echo "Запрос возвратил ошибку: (" . $e->getCode() . ") " . $e->getMessage() . "\n";
			die();
		}
	}
	
	/**
	 * Метод получения строки DSN для создания объекта PDO
     *
	 * @return string
 	 */
	private function getDSN() {

		if(!($this->_drv) || ($this->_drv == "mysql"))

			return "mysql:dbname=;host=$this->_host";

		else

			return "$this->_drv:dbname=$this->_dbname;host=$this->_host";

	}

}

?>
